G1 GC是一种自适应垃圾收集算法,自Java 9以来已成为默认的GC算法。今天主要通过分享一些简单的技巧来调整G1垃圾收集器以获得最佳运行性能。 5、熟悉默认配置 为了进行适配调整,在下表中,我们总结了重要的G1 GC算法参数及其默认值: G1 GC参数 参数描述 -XX:MaxGCPauseMillis = 200 设置最大暂停时间值 6、探究GC Cause 优化G1 GC性能的有效方法之一是研究触发GC的原因并结合实际的业务场景提供解决方案以减少它们的发生。 这些工具能够结合应用服务运行状态以及当前的环境详细展示出触发GC活动的原因。以下是G1 GC日志文件时借助其他工具生成的GC原因表。 6.2、G1 疏散暂停或疏散失败 当我们看到G1撤离暂停时,则G1 GC没有足够的内存来存储幸存者或被提升的物体,或两者都没有。Java堆无法扩展,因为它已经达到了最大值。
那么问题来了,既然我们已经有了上面三个强大的GC,为什么还要发布Garbage First(G1)GC? G1 GC基本思想 G1 GC是一个压缩收集器,它基于回收最大量的垃圾原理进行设计。G1 GC利用递增、并行、独占暂停这些属性,通过拷贝方式完成压缩目标。 这是G1 GC和之前的几代GC的一大差别。 G1 GC的垃圾回收循环由三个主要类型组成: 年轻代循环 多步骤并行标记循环 混合收集循环 Full GC 在年轻代回收期,G1 GC暂停应用程序线程,然后从年轻代区间移动存活对象到Survivor区间或者老年区间 本书具体来说包括以下几方面:JVM基础知识、GC基础知识、G1 GC深入介绍、G1 GC调优建议、JDK自带工具使用介绍等。 相关图书推荐,《深入理解JVM & G1 GC》
在 Java9 中,G1 GC 将成为默认的垃圾收集器,G1 垃圾收集器的关键特性之一是能够在不牺牲吞吐量的同时,限制 GC 暂停时间(即可以设置所需的最大停顿时间)。 由于 G1 GC 正在逐渐成为默认的垃圾收集器,它的使用与关注度也会逐渐增加。 因此在调整 JVM 大小和排查问题的情况下,必须先理解 G1 GC 的日志格式,接下来将介绍如何理解 G1 GC 的日志格式。 Minor GC 日志 当发生 Minor GC 时,在 GC 日志文件里会有以下内容: ? 上图展示了在 G1 垃圾收集日志中的 Young GC 事件。 Full GC 日志 当发生 Full GC 时,在 GC 日志文件里会有以下内容: ? 上图展示了在 G1 垃圾收集日志中的 Full GC 事件。
序 本文主要研究一下G1 GC的String Deduplication the-performance-engineers-guide-to-java-hotspot-virtual-machine -76-638.jpg -XX:+UseStringDeduplication jdk8u20给G1 GC带来了String Deduplication特性来将相同的字符串指向同一份数据,来减少重复字符串的内存开销 (前提是使用-XX:+UseG1GC) 在有大量重复string的前提下,使用G1 GC开启String Deduplication确实能够节省一定的内存,可以节约20%左右的内存,不过这个是理想的情况 Strings: How to Get Rid of Them and Save Memory String deduplication feature (from Java 8 update 20) G1 GC: Reducing Memory Consumption by 20%
此前,我们已经深入介绍了 g1 垃圾回收的机制以及相应的配置: 驾驭一切的垃圾收集器 -- G1 本文,我们就来详细介绍一下 g1 的 gc log,让你能够通过 g1 的 gc log 分析出性能问题出在哪里 当然,上文介绍的两个实用的 gc log 分析工具:GCeasy 与 GC Viewer,都可以直接用于分析 g1 的 gc log,本文就不再赘述了。 2. GC log 相关的参数配置 2.1 日志打印的相关参数 上一篇文章中介绍的 gc 日志打印的相关参数在 g1 垃圾回收器中仍然是可用的: -Xloggc:<path> # gc log 的输出路径 - G1 的 gc log 4.1 young gc 如图所示,相较于 CMS 的 GC 日志,G1 的 GC 日志信息更为详细。 4.4 FULL GC 在 G1 中,full gc 是我们要极力避免的,他是整个堆内存的完整收集,因此,G1 中的 full gc 通常意味着一个漫长的 stw 暂停。
G1 GC的内部结构 从内存区域的角度,G1 同样存在着年代的概念,但是与前面在堆内存划分中讲的很不一样,其内部是类似棋盘状的一个个 region 组成,请参考下面的示意图。 G1 GC中的分代收集 习惯上人们喜欢把新生代 GC(Young GC)叫作 Minor GC,老年代 GC 叫作 Major GC,区别于整体性的 Full GC。 但是现代 GC 中,这种概念已经不再准确,对于 G1 来说: Minor GC 仍然存在,虽然具体过程会有区别,会涉及 Remembered Set 等相关处理。 如何优化 G1 GC? 建议尽量升级到较新的 JDK 稳定版本. 掌握 GC 调优信息收集途径。掌握尽量全面、详细、准确的信息,是各种调优的基础,不仅仅是 GC 调优。 和 G1 GC 相关的日志配置 除了常用的两个选项, -XX:+PrintGCDetails -XX:+PrintGCDateStamps 还有一些非常有用的日志选项,很多特定问题的诊断都是要依赖这些选项
JEP 192 :G1的String去重 ? 当我们使用G1 GC时,它会从内存中删除垃圾对象。 它还从内存中删除重复的字符串对象,叫做string deduplication(字符串去重)。 而且只需要通过传递以下JVM参数就可以激活此功能: -XX:+UseG1GC -XX:+UseStringDeduplication Note 1:要想使用此功能,你需要把你的Java升级到Java Note 2:“ -XX:+UseStringDeduplication” 参数是运行在G1之上的,所以你需要在G1下使用此参数才会生效。 例子 接下来用一个简单的程序来验证此功能。 即: -Xmx20M -XX:+UseG1GC -XX:+UseStringDeduplication Run#2 第二次运行我们去掉 ‘-XX:+ UseStringDeduplication’ 来运行程序 因此,鼓励大家多使用“-XX:+ UseG1GC -XX:+ UseStringDeduplication”,这样可以减少由于重复字符串而引起的内存浪费。这样做有可能能够降低应用程序的整体内存占用量。
基础参数 如果你要在生产环境中使用G1 GC,下面这些跟日志相关的参数是必备的,有了这些参数,你才能排查基本的垃圾回收问题。 ? G1 GC的基础参数 使用-XX:GCLogFileSize设置合适的GC日志文件大小,使用-XX:NumberOfGCLogFiles设置要保留的GC日志文件个数,使用-Xloggc:/path/to G1收集器的并发垃圾收集日志 标志着并发垃圾收集阶段的开始: GC pause(G1 Evacuation Pause)(young)(initial-mark):为了充分利用STW的机会来trace G1的混合收集 Full GC 如果堆内存空间不足以分配新的对象,或者是Metasapce空间使用率达到了设定的阈值,那么就会触发Full GC——你在使用G1的时候应该尽量避免这种情况发生,因为G1的 G1的Full GC 基础配置参数中,我这里还想介绍两个:-XX:+PrintGCApplicationStoppedTime和-XX:+PrintGCApplicationConcurrentTime
本文则重点介绍另一款当前比较流行的 GC 策略 - G1。 Heap 其实,针对 G1 GC 优化,在之前的文章中有所涉及,大家有兴趣可以查阅,链接为:G1 GC简单优化技巧。 GC 阶段 通常,在基于 G1 GC 策略场景中,主要存在3个核心的垃圾收集策略。(严格声明:JDK 10 之前的 G1中的 GC 只有 Young GC 和 Mixed GC。 其实,从本质上讲,JDK 8 版本的 G1 是不提供 Full GC 事件的处理。G1 GC 和其他分代垃圾收集器的主要目标之一:避免使用昂贵的 Full GC。 毕竟,在 Java 8上,默认 GC 是 CMS GC,而在 Java 11 上,默认 GC 才是 G1 GC。
G1的垃圾收集模式 G1中有两种回收模式: 1.完全年轻代GC(fully-young collection),也称年轻代垃圾回收(Young GC)2.部分年轻代GC(partially-young collection)又称混合垃圾回收(Mixed GC) 年轻代垃圾回收(完全年轻代GC) 完全年轻代GC是只选择年轻代区域(Eden/Survivor)进入回收集合(Collection Set,简称 年轻代GC的过程和其他的分代回收器差不多,新创建的对象分配至Eden区域,然后将标记存活的对象移动至Survivor区,达到晋升年龄的就晋升到老年代区域,然后清空原区域(不过这里可没有年轻代复制算法中两个 年轻代GC会选择所有的年轻代区域加入回收集合中,但是为了满足用户停顿时间的配置,在每次GC后会调整这个最大年轻代区域的数量,每次回收的区域数量可能是变化的 下面是一个完全年轻代GC过程的简单示意图:将选择的年轻代区域中所有存活的对象 选择收集集合(Choose CSet),G1会在遵循用户设置的GC暂停时间上限的基础上,选择一个最大年轻带区域数,将这个数量的所有年轻代区域作为收集集合。
Full GC 当混合回收无法跟上内存分配的速度,导致老年代也满了,就会进行Full GC对整个堆进行回收。 G1中的Full GC也而是单线程串行的,而且是全暂停,使用的是标记-整理算法,代价非常高。 暂停时间的控制 G1在移动过程中虽然也是全暂停,不过G1在选择回收集合上是变化的,每次只选择部分的区域进行回收,通过计算每个区域的预测暂停时间来保证每次回收所占用的时间。 简单的说就是将一次完整的GC拆分成多次短时间的GC从而降低暂停的时间,尽量保证每次的暂停时间在用户的配置范围(-XX:MaxGCPauseMilli)内。 所以G1中尽量不要设置年轻代的大小,让G1自动的进行调整
混合回收(Mixed GC) 混合回收,会选择所有年轻代区域(Eden/Survivor)(最大年轻代分区数)和部分老年代区域进去回收集合进行回收的模式。 由于G1中老年代区域的回收方式和新生代一样是“移动式”,被回收区域在移动后会全部清空,所以不会像其他使用清除算法的回收器一样(比如CMS)有碎片问题。 下面是一个Mixed GC过程的简单示意图: ? 初始标记(Initial Mark) 标记由根直接引用的对象(STW),这个过程是在年轻代GC中完成的,不过不是每次年轻代GC都会进行初始标记。 并发标记(Concurrent Mark) 以步骤1的标记结果作为root,遍历可达的对象进行标记,和mutator并行,并且可被年轻代GC中断,年轻代GC完成后可继续进行标记 最终标记 (Remark
//并发标记 - 初始标记阶段,在年轻代GC中完成 100.070: [GC pause (G1 Evacuation Pause) (young) (initial-mark), 0.0751469 secs] [Parallel Time: 74.7 ms, GC Workers: 8] [GC Worker Start (ms): Min: 100070.4, Avg: 100070.5 ,包括再标记、引用处理、类卸载处理信息 100.254: [GC remark 100.254: [Finalize Marking, 0.0002228 secs] 100.254: [GC ref-proc secs] 混合回收日志 // 混合回收Mixed GC其实和YGC的日志类似,能看到GC pause(G1EvacuationPause)(mixed)这样的信息 // 日志分析参考Y年轻代GC。 122.132: [GC pause (G1 Evacuation Pause) (mixed), 0.0106092 secs] [Parallel Time: 9.8 ms, GC Workers
这个参数会限制metaspace(包括了Klass Metaspace以及NoKlass Metaspace)被committed的内存大小,会保证committed的内存不会超过这个值,一旦超过就会触发GC 3.MaxDirectMemorySize 此参数主要影响的是非堆内存的direct byte buffer,jvm默认会设置64M,可根据功能适当加大此项参数,因为非堆内存,故而不会被GC回收掉,容易出现 log,之后用工具进行分析 -XX:-HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=logs/oom_dump.log 4.G1收集器参数 -XX:+UseG1GC 使用G1收集器 -XX:MaxGCPauseMillis=200 用户设定的最大gc 停顿时间,默认是200ms. -XX:G1HeapRegionSize=n 使用G1时Java堆会被分为大小统一的区(region)。此参数可以指定每个heap区的大小. 默认值将根据 heap size 算出最优解.
G1 GC是一个响应时间优先的GC算法,它与CMS最大的不同是,用户可以设定整个GC过程的期望停顿时间,参数-XX:MaxGCPauseMillis指定一个G1收集过程目标停顿时间,默认值200ms,不过它不是硬性条件 GC过程 讲完了一些基本概念,下面我们就来看看G1的GC过程是怎样的。 G1 GC模式 G1提供了两种GC模式,Young GC和Mixed GC,两种都是完全Stop The World的。 所以我们可以知道,G1是不提供full GC的。 [GC pause (G1 Evacuation Pause) (young) GC原因,新生代minor GC。 Tuning the Garbage First Garbage Collector g1gc-impl-book 垃圾优先型垃圾回收器调优 Understanding G1 GC Logs G1: One
怎么想、怎么做,全在乎自己「不断实践中寻找适合自己的大道」 0 前言 G1,Java8可选,Java9已默认的垃圾收集器,G1 垃圾收集器的关键特性之一是能够在不牺牲吞吐量的同时,限制 GC 暂停时间 调优 JVM 大小和排查问题的情况下,须理解 G1 GC 的日志格式。 由于 G1 GC 日志中有许多与子任务相关的信息,因此为了更好地理解和利用这些信息,推荐 GC 日志分析工具:http://gceasy.io/。 1 生成详细的GC log 可用如下参数打开 GC 日志: -Xloggc:/home/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps GC 日志会写入 2.2 GC pause (G1 Evacuation Pause) 疏散停顿(Evacuation Pause):将存活对象从一个区域(young or young + old)拷贝到另一个区域的阶段
G1 GC是一个响应时间优先的GC算法,它与CMS最大的不同是,用户可以设定整个GC过程的期望停顿时间,参数-XX:MaxGCPauseMillis指定一个G1收集过程目标停顿时间,默认值200ms,不过它不是硬性条件 GC过程 讲完了一些基本概念,下面我们就来看看G1的GC过程是怎样的。 G1 GC模式 G1提供了两种GC模式,Young GC和Mixed GC,两种都是完全Stop The World的。 所以我们可以知道,G1是不提供full GC的。 [GC pause (G1 Evacuation Pause) (young) GC原因,新生代minor GC。 Tuning the Garbage First Garbage Collector g1gc-impl-book 垃圾优先型垃圾回收器调优 Understanding G1 GC Logs G1: One
在 G1 中,Java 就对此功能做了支持。 G1 做了什么? G1 GC 算法运行时,它将从内存中删除垃圾对象。它还从内存中删除重复的字符串对象(字符串重复数据删除)。 注意2:为了使用 -XX:+UseStringDeduplication ,您需要使用 G1 GC 算法。 因此,你可以利用 -XX:+UseG1GC-XX:+UseStringDeduplication 来减少重复字符串导致的内存浪费,它会减少应用程序的整体内存占用。
//[GC pause (G1 Evacuation Pause) (young) 代表完全年轻代回收 // 0.0182341 secs 是本次GC的暂停时间 0.184: [GC pause (G1 Evacuation Pause) (young), 0.0182341 secs 是本次GC的暂停时间] // 并行GC线程,一共有8个 [Parallel Time: 16.7 ms, GC 线程启动的时间依赖于GC进入安全点的情况。关于安全点可以参考后文的介绍。 */ [GC Worker Start (ms): 184.2 184.2 184.2 184.3 184.3 184.4 186.1 186.1 Min: 184.2 , Avg: 16.1, Max: 16.6, Diff: 1.9, Sum: 128.7] // GC线程结束的时间信息 [GC Worker End (ms): 200.8
在 G1 中,Java 就对此功能做了支持。 G1 做了什么? G1 GC 算法运行时,它将从内存中删除垃圾对象。它还从内存中删除重复的字符串对象(字符串重复数据删除)。 可以通过设置以下 JVM 参数来激活此功能: -XX:+UseG1GC -XX:+UseStringDeduplication 注意1:为了使用此功能, 需要在 Java 8 update 20 或更高版本上运行 注意2:为了使用 -XX:+UseStringDeduplication,您需要使用 G1 GC 算法。 : -Xmx20M -XX:+UseG1GC 在这两次运行中,我们都捕获了堆的 Dump 信息,并通过堆的 Dump 分析工具 HeapHero.io 对其进行了分析。 因此,你可以利用 -XX:+UseG1GC-XX:+UseStringDeduplication来减少重复字符串导致的内存浪费,它会减少应用程序的整体内存占用。